#include "../include/terrain.hpp"
#include <stdlib.h>
#include <limits.h>
#include <time.h>
using namespace std;

void Terrain::calcul()
{
    string use;
    bool premier_pass = true;
    bool ok;
    unsigned int i=0;
    //au début vider les parcelles
    ligneCourante = new int[lecteur->getNbCol()];
    ecriture = new int[lecteur->getNbCol()];
    lesParcelles_up= new Parcelle[lecteur->getNbCol()];
    lesParcelles_center = new Parcelle[lecteur->getNbCol()];
    lesParcelles_down = new Parcelle[lecteur->getNbCol()];
    lesParcelles_downdown = new Parcelle[lecteur->getNbCol()];
    Parcelle * aux;
    lignes_traitees=0;
    //clock_t start, end;
    //double elapsed;
    //start = clock();
    unsigned int compteur=0;
    //tant que on a pas fini le fichier
    while(lignes_traitees < lecteur->getNbLig())
    {
        //vider les parcelles mais pas la dernière ligne
            if((lignes_traitees * 100)/lecteur->getNbLig() > compteur)
            {
                compteur++;
                cout<<compteur<<"%"<<endl;
            }


        if (!premier_pass)
        {
            //déja des lignes, on charge la dernière
            if (lesParcelles_up != NULL)
            {
                delete [] lesParcelles_up;
                lesParcelles_up = lesParcelles_center;
                lesParcelles_center = lesParcelles_down;
                lesParcelles_down= lesParcelles_downdown;

            }
            //on charge deux lignes de plus

            ok=lecteur->getNextLine(ligneCourante);
            aux = new Parcelle[lecteur->getNbCol()];
            lesParcelles_downdown = aux;
            for (unsigned int itzz = 0; itzz < lecteur->getNbCol(); itzz++)
            {
                //pour tout int de ligneCourante creer une parcelle
                lesParcelles_downdown[itzz] = Parcelle(ligneCourante[itzz],itzz,lignes_traitees);
            }
            //on a traité un ligne donc ligne traitees++
            lignes_traitees++;

            for (i=0; i< lecteur->getNbCol() ; i++)
            {
               filtreCalculCuvette(lesParcelles_center[i]);

            }
            //un fois le calcul fais, on rajoute une line au lecteur pour ecrire le resultas
            for(unsigned int e0 = 0 ; e0 < lecteur->getNbCol(); e0++)
            {
                ecriture[e0]=lesParcelles_center[e0].getNumBassin();
            }
            lecteur->addLine(ecriture);
            /*if(MAJBassin.size() > 30000)
                lecteur->switchVal(MAJBassin);*/
        }
        else
        {
            //premier passage effectué
            premier_pass = false;
            //Les parcelles vides, remplir avec les trois premières lignes
            //premiere ligne
            ok=lecteur->getNextLine(ligneCourante);
            for (unsigned int itlig=0; itlig< lecteur->getNbCol(); itlig++)
            {
                //pour tout int de ligneCourante creer une parcelle
                lesParcelles_up[itlig] = Parcelle(ligneCourante[itlig],itlig,lignes_traitees);

            }
            //on a traité un ligne donc ligne traitees++
            lignes_traitees++;
            //seconde ligne
            ok=lecteur->getNextLine(ligneCourante);
            for (unsigned int itligg=0; itligg< lecteur->getNbCol(); itligg++)
            {
                lesParcelles_center[itligg] = Parcelle(ligneCourante[itligg],itligg,lignes_traitees);

            }
            //on a traité un ligne donc ligne traitees++
            lignes_traitees++;
            //troisieme ligne
            ok=lecteur->getNextLine(ligneCourante);
            for (unsigned int itliggg=0; itliggg< lecteur->getNbCol(); itliggg++)
            {
                lesParcelles_down[itliggg] = Parcelle(ligneCourante[itliggg],itliggg,lignes_traitees);
            }
            lignes_traitees++;

            ok=lecteur->getNextLine(ligneCourante);
            for(unsigned int itligggg=0 ; itligggg < lecteur->getNbCol(); itligggg++)
            {
                lesParcelles_downdown[itligggg] = Parcelle(ligneCourante[itligggg],itligggg,lignes_traitees);
            }
            //on a traité un ligne donc ligne traitees++
            lignes_traitees++;
            // on lance le calcul de cuvette sur toute les parcelles
            for (i=0; i< lecteur->getNbCol() ; i++)
            {
                filtreCalculCuvette(lesParcelles_up[i]);
            }
            for (i=0; i< lecteur->getNbCol() ; i++)
            {
                filtreCalculCuvette(lesParcelles_center[i]);
            }
            //on retranscrit la première ligne
            for (unsigned int e3 =0 ; e3 < lecteur->getNbCol() ; e3++)
            {
                ecriture[e3] = lesParcelles_up[e3].getNumBassin();
            }
            lecteur->addLine(ecriture);
            //on retranscrit la seconde ligne
            for (unsigned int e4 = 0 ; e4 < lecteur->getNbCol() ; e4++)
            {
                ecriture[e4]= lesParcelles_center[e4].getNumBassin();
            }
            lecteur->addLine(ecriture);
        }
    }
    //on a fini de charger
    for (i=0; i< lecteur->getNbCol() ; i++)
    {
        filtreCalculCuvette(lesParcelles_down[i]);
    }
    for(unsigned int e9 = 0 ; e9 < lecteur->getNbCol(); e9++)
    {
        ecriture[e9]=lesParcelles_down[e9].getNumBassin();
    }
    lecteur->addLine(ecriture);
    for (i=0; i< lecteur->getNbCol() ; i++)
    {
        filtreCalculCuvette(lesParcelles_downdown[i]);
    }
    for(unsigned int e19 = 0 ; e19 < lecteur->getNbCol(); e19++)
    {
        ecriture[e19]=lesParcelles_downdown[e19].getNumBassin();
    }
    lecteur->addLine(ecriture);
    cout<<"calcul termine. Debut de la defragmentation."<<endl;
    lecteur->stockMap(MAJBassin);
    lecteur->defragmap();

}


inline void Terrain::calculCuvette(Parcelle *p)
{
    if(p->getHauteur() != lecteur->getNoValue())
    {

        Parcelle* aux  = p ;
        Parcelle* aux2 = new Parcelle(0,0,0);
        Parcelle* cv = aux2;
        bool fixe = false;

        while (!fixe && aux2->getNumBassin()== -1)
        {
            aux2 = calculCible(aux);
            if (*aux2 == *aux)
            {
                fixe = true;
            }
            aux =aux2;
        }


        // CAS OU ON DECOUVRE UN NOUVEAU BASSIN
        if (p->getNumBassin() ==-1 && aux->getNumBassin() ==-1)
        {
            this->setNbBassin(this->getNbBassin()+1);
            // on donne au nouveau puit et a la Parcelle le numbassin
            p->setNumBassin(this->getNbBassin());
            aux->setNumBassin(this->getNbBassin());

            bassin_t b = {aux->getNumBassin(),*aux};
            lesBassins->push_back(b);
            // on ajoute le nouveau puit dnas le vecteur de puit
        }

        //soit la cuvette as un num et pas p
        else if (p->getNumBassin() ==-1 && aux->getNumBassin() != -1 )
        {
            p->setNumBassin(aux->getNumBassin());
        }

        // soit la cuvette a un num et la parcelle aussi;
        else if(p->getNumBassin() != -1 && aux->getNumBassin() != -1)
        {
            //pensez que si on a un num, on peut être un bassin
            //si dans les bassins, alors mettre l'ancien num bassin
            /*for(vector<bassin_t>::iterator it = lesBassins->begin(); it != lesBassins->end(); it++)
            {
                if((*it).puit == *p)
                {

                    //MAJBassin[p->getNumBassin()]=aux->getNumBassin();
                }
            }*/
            p->setNumBassin(aux->getNumBassin());
        }//soit cas de case frontiere, p a un num et pas aux
        else if ( p->getNumBassin() != -1 && aux->getNumBassin() == -1)
        {
            aux->setNumBassin(getNbBassin());
            p->setNumBassin(aux->getNumBassin());
            setNbBassin(getNbBassin()+1);
        }
        delete cv;
        p = calculCible(p);
   }
    else
    {
        p->setNumBassin(p->getNumBassin());
    }
}


void Terrain::afficheLesBassins()
{
    vector<bassin_t>::iterator it;
    for(it = this->getLesBassins()->begin(); it != this->getLesBassins()->end(); it++)
    {
        cout<<"|"<<(*it).id<<"|"<<(*it).puit.getAbs()<<"|"<<(*it).puit.getOrd()<<endl;
    }
}


void Terrain::calculRegroupBassin()
{
    //on vide la MAJ
    vector<bassin_t>::iterator it;
    vector<bassin_t>::iterator aux;
    for(it=this->getLesBassins()->begin()++; it!=this->getLesBassins()->end(); it++)
    {
        // pour toutes les case du vector lesBassins
        for(aux=this->getLesBassins()->begin(); aux!=it; aux++)
        {
            if((*aux).puit == (*it).puit)
            {
                // ON ENREGISTRE LA MODIF IT SERA REMPLAC2 PAR AUX
                MAJBassin[(*it).id]=(*aux).id;
                // ON SUPPRIME IT DU VECTOR
                it=lesBassins->erase(it);
                //it++;
            }
        }

        //pour chaque case du vector les bassins , on regarde s'il n'as pas une cuvette commune avec ceux d'avant
    }
    // on a regrouper tous les bassins
    //lecteur->switchVal(MAJBassin);
}

inline Parcelle* Terrain::calculCible(Parcelle *p)
{
    if( p->getHauteur() == lecteur->getNoValue())
    {
        return p;
    }
    bool ok = false;
    Parcelle * safe;
    Parcelle *res = new Parcelle(INT_MAX,0,0);
    safe = res;
    Parcelle* current = NULL;
    unsigned int abs, ord, nbcol;
    int noval = lecteur->getNoValue(), hauteur = res->getHauteur();
    abs=p->getAbs();ord=p->getOrd();nbcol=lecteur->getNbCol();

    // GARE AUX BORDS

    //basdroite bas basgauche droite centre gauche hautdroit haut hautgauche
    //basdroite bas droite centre basgauche hautdroite gauche haut hautgauche

    //si collee au bas
    if(ord < lignes_traitees-1)
    {

        // bas droite
        if ( abs <nbcol-1)
        {
            current=getParcelle(abs+1, ord+1);
            if(current->getHauteur() < hauteur && current->getHauteur() != noval)
            {
                res = current;
                hauteur = res->getHauteur();
                ok = true;
            }
        }
        // bas
        current=getParcelle(abs, ord+1);
        if(current->getHauteur() < hauteur && current->getHauteur() != noval)
        {
            res = current;
            hauteur = res->getHauteur();ok = true;
        }
            // bas gauche
        if (abs >=1)
        {
            current=getParcelle(abs-1, ord+1);
            if(current->getHauteur() < hauteur && current->getHauteur() != noval)
            {
                res = current;
                hauteur = res->getHauteur();ok = true;
            }
        }
    }
// droite
    if (abs < nbcol-1)
    {
        current=getParcelle(abs+1, ord);
        if(current->getHauteur() <= hauteur && current->getHauteur() != noval)
        {
            res = current;
            hauteur = res->getHauteur();ok = true;
        }
    }

    //centre
    current=getParcelle(abs,ord);
    if(current->getHauteur() <= hauteur && current->getHauteur() != noval)
    {
        res = p;
        hauteur = res->getHauteur();ok = true;
    }

    // gauche
    if (abs >= 1)
    {
        current=getParcelle(abs-1, ord);
        if(current->getHauteur() <= hauteur && current->getHauteur() != noval)
        {
            res = current;
            hauteur = res->getHauteur();ok = true;
        }
    }
    //si haut
    if(ord >lignes_traitees-4)
    {
            // haut droite
        if (abs < nbcol-1)
        {
            current=getParcelle(abs+1, ord-1);
            if(current->getHauteur() <= hauteur && current->getHauteur() != noval)
            {
                res = current;
                hauteur = res->getHauteur();ok = true;
            }
        }
        // haut
        current=getParcelle(abs, ord-1);
        if(current->getHauteur() <= hauteur && current->getHauteur() != noval)
        {
            res = current;
            hauteur = res->getHauteur();ok = true;
        }

        // haut gauche
        if (abs >=1)
        {
            current=getParcelle((abs)-1, (ord)-1);
            if(current->getHauteur() <= hauteur && current->getHauteur() != noval)
            {
                res = current;
                hauteur = res->getHauteur();ok = true;
            }
        }
    }
    if(ok)
        delete safe;
    return res;
}


void Terrain::filtreCalculCuvette(Parcelle & p)
{
    if(p.getNumBassin() == -1)
    {
        calculCuvette(&p);
    }
    else if(calculCible(&p)->getNumBassin() != p.getNumBassin())
    {
        Parcelle * troll = calculCible(&p);
        int cpt = 0;
        while(troll->getNumBassin() == -1 && cpt < 60)
        {
            troll = calculCible(troll);
            cpt++;
        }
        if(troll->getNumBassin() != -1)
        {
            map<int,int>::iterator it;
            it=MAJBassin.find(p.getNumBassin());
            if(!(it!= MAJBassin.end() && MAJBassin[p.getNumBassin()]!=troll->getNumBassin()))
            {
                MAJBassin[p.getNumBassin()] = troll->getNumBassin(); lecteur->fragincr();
                if(MAJBassin.size() > 99999)
                {
                    lecteur->majmap(MAJBassin);
                    lecteur->stockMap(MAJBassin);
                }
            }
        }
    }
}
